#include "avs4x264mod.h"

avs_hnd_t avs_h;
AVS_Value arg;
AVS_Value res;
buffer_t OBuffer = {0};

/* load the library and functions we require from it */
static int avs_load_library( avs_hnd_t *h )
{
	h->library = LoadLibrary( "avisynth" );
	if( !h->library )
		return -1;
	LOAD_AVS_FUNC( avs_clip_get_error, 0 );
	LOAD_AVS_FUNC( avs_create_script_environment, 0 );
	LOAD_AVS_FUNC( avs_delete_script_environment, 1 );
	LOAD_AVS_FUNC( avs_get_frame, 0 );
	LOAD_AVS_FUNC( avs_get_version, 0 );
	LOAD_AVS_FUNC( avs_get_video_info, 0 );
	LOAD_AVS_FUNC( avs_function_exists, 0 );
	LOAD_AVS_FUNC( avs_invoke, 0 );
	LOAD_AVS_FUNC( avs_release_clip, 0 );
	LOAD_AVS_FUNC( avs_release_value, 0 );
	LOAD_AVS_FUNC( avs_release_video_frame, 0 );
	LOAD_AVS_FUNC( avs_take_clip, 0 );
	return 0;
fail:
	FreeLibrary( h->library );
	return -1;
}

int LoadAVSFile(video_info_t * VideoInfo)
{
	int avs_version;
	//avs open
	if( avs_load_library( &avs_h ) )
	{
		fprintf( stderr, "avs [error]: failed to load avisynth\n" );
		return ERR_AVS_NOTFOUND;
	}
	avs_h.env = avs_h.func.avs_create_script_environment( AVS_INTERFACE_YV12 );
	if( !avs_h.env )
	{
		fprintf( stderr, "avs [error]: failed to initiate avisynth\n" );
		return ERR_AVS_FAIL;
	}
	fprintf( stderr, "avs [info]: Trying to open %s\n", VideoInfo->infile );
	arg = avs_new_value_string( VideoInfo->infile );

	res = avs_h.func.avs_invoke( avs_h.env, "Import", arg, NULL );
	if( avs_is_error( res ) )
	{
		fprintf( stderr, "avs [error]: %s\n", avs_as_string( res ) );
		return ERR_AVS_FAIL;
	}
	
	/* check if the user is using a multi-threaded script and apply distributor if necessary.
		adapted from avisynth's vfw interface */
	AVS_Value mt_test = avs_h.func.avs_invoke( avs_h.env, "GetMTMode", avs_new_value_bool( 0 ), NULL );
	int mt_mode = avs_is_int( mt_test ) ? avs_as_int( mt_test ) : 0;
	avs_h.func.avs_release_value( mt_test );
	if( mt_mode > 0 && mt_mode < 5 )
	{
		AVS_Value temp = avs_h.func.avs_invoke( avs_h.env, "Distributor", res, NULL );
		avs_h.func.avs_release_value( res );
		res = temp;
	}

	if( !avs_is_clip( res ) )
	{
		fprintf( stderr, "avs [error]: `%s' didn't return a video clip\n", VideoInfo->infile );
		return ERR_AVS_FAIL;
	}
	avs_h.clip = avs_h.func.avs_take_clip( res, avs_h.env );
	avs_version = avs_h.func.avs_get_version( avs_h.clip );
	const AVS_VideoInfo *vi = avs_h.func.avs_get_video_info( avs_h.clip );
	if( !avs_has_video( vi ) )
	{
		fprintf( stderr, "avs [error]: `%s' has no video data\n", VideoInfo->infile );
		return ERR_AVS_FAIL;
	}
	if( vi->width&1 || vi->height&1 )
	{
		fprintf( stderr, "avs [error]: input clip width or height not divisible by 2 (%dx%d)\n",
				 vi->width, vi->height );
		return ERR_AVS_FAIL;
	}
	/* always call ConvertToYV12 to convert non YV12 planar colorspaces to YV12 when user's AVS supports them,
		as all planar colorspaces are flagged as YV12. If it is already YV12 in this case, the call does nothing */
	if( !avs_is_yv12( vi ) || avs_version >= AVS_INTERFACE_OTHER_PLANAR )
	{
		avs_h.func.avs_release_clip( avs_h.clip );
		fprintf( stderr, "avs %s\n", !avs_is_yv12( vi ) ? "[warning]: converting input clip to YV12"
			: "[info]: Avisynth 2.6+ detected, forcing conversion to YV12" );
		const char *arg_name[2] = { NULL, "interlaced" };
		AVS_Value arg_arr[2] = { res, avs_new_value_bool( VideoInfo->b_interlaced ) };
		AVS_Value res2 = avs_h.func.avs_invoke( avs_h.env, "ConvertToYV12", avs_new_value_array( arg_arr, 2 ), arg_name );
		if( avs_is_error( res2 ) )
		{
			fprintf( stderr, "avs [error]: Couldn't convert input clip to YV12\n" );
			return ERR_AVS_FAIL;
		}
		avs_h.clip = avs_h.func.avs_take_clip( res2, avs_h.env );
		avs_h.func.avs_release_value( res2 );
		vi = avs_h.func.avs_get_video_info( avs_h.clip );
	}
	fprintf( stderr, "avs [info]: Video %dx%d\n", vi->width, vi->height );
	avs_h.func.avs_release_value( res );

	VideoInfo->i_width = vi->width;
	VideoInfo->i_height = vi->height;
	VideoInfo->i_fps_num = vi->fps_numerator;
	VideoInfo->i_fps_den = vi->fps_denominator;
	VideoInfo->i_frame_total = vi->num_frames;
}
